package com.rentdvd.ws;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.rentdvd.entity.User;
import com.rentdvd.util.HibernateUtil;

import NET.webserviceX.www.GlobalWeatherSoapProxy;

@WebService(endpointInterface = "com.rentdvd.ws.RentDVD")
public class RentDVDImpl implements RentDVD {

	@Override
	public boolean register(String username, String password) {
		User user = new User();
		user.setUsername(username);
		user.setPassword(password);

		SessionFactory sf = HibernateUtil.getSessionFactory();
		Session session = sf.openSession();
		session.beginTransaction();

		try {
			session.save(user);
		} catch (Exception e) {
			return false;
		}

		session.getTransaction().commit();
		session.close();

		return true;
	}

	public boolean authenticate(String name, String password) {
		SessionFactory sf = HibernateUtil.getSessionFactory();
		Session session = sf.openSession();

		Query query = session.createQuery("select u from User u where u.username=:username");
		query.setParameter("username", name);

		List list = query.list();

		if (!list.isEmpty()) {
			User u = (User) list.get(0);
			return u.getPassword().equals(password);
		}

		return false;
	}

	public String getCatalog() {
		StringBuilder sb = new StringBuilder();

		InputStream resourceAsStream = this.getClass().getResourceAsStream("/catalog.xml");
		try {
			if (resourceAsStream != null && resourceAsStream.available() > 0) {
				Scanner s = new Scanner(resourceAsStream);
				s.nextLine();
				while (s.hasNext()) {
					sb.append(s.nextLine());
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return sb.toString();
	}

	public String recommendMovie(String userName, String city) {
		List<Integer> watchedFilms = getWatchedFilms(userName);

		GlobalWeatherSoapProxy gwsp = new GlobalWeatherSoapProxy();
		String temp = null;
		try {
			String result = gwsp.getWeather(city, "");

			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.parse(new InputSource(new StringReader(result)));

			XPathFactory xPathfactory = XPathFactory.newInstance();
			XPath xpath = xPathfactory.newXPath();
			XPathExpression expr = xpath.compile("//Temperature");
			temp = expr.evaluate(doc);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		if (temp == null || temp.isEmpty()) {
			return "";
		}

		int from = temp.indexOf("(");
		int to = temp.indexOf(")");
		double temperature = Double.parseDouble(temp.substring(from + 1, to - 1).trim());

		InputStream resource = this.getClass().getResourceAsStream("/catalog.xml");
		String result = "";

		try {
			DocumentBuilderFactory dbFactory2 = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory2.newDocumentBuilder();
			Document doc2 = dBuilder.parse(resource);

			XPathFactory xPathfactory2 = XPathFactory.newInstance();
			XPath xpath = xPathfactory2.newXPath();
			XPathExpression expr2 = null;

			if (temperature > 10) {
				expr2 = xpath.compile("/movies/movie[genre='comedy' or genre='romance']");
			} else if (temperature >= 5) {
				expr2 = xpath.compile("//movie[genre='action']");
			} else {
				expr2 = xpath.compile("//movie[genre='drama']");
			}

			NodeList list = (NodeList) expr2.evaluate(doc2, XPathConstants.NODESET);

			for (int i = 0; i < list.getLength(); i++) {
				Node node = list.item(i);
				String id = node.getAttributes().getNamedItem("id").getTextContent();

				if (!watchedFilms.contains(Integer.valueOf(id))) {
					TransformerFactory transFactory = TransformerFactory.newInstance();
					Transformer transformer = transFactory.newTransformer();
					StringWriter buffer = new StringWriter();
					transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
					transformer.transform(new DOMSource(node), new StreamResult(buffer));
					result += buffer.toString();
				}
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}

		return result.toString();
	}

	@Override
	public boolean addWatchedFilms(String userName, Integer[] filmArray) {
		SessionFactory sf = HibernateUtil.getSessionFactory();
		Session session = sf.openSession();
		session.beginTransaction();

		Query query = session.createQuery("select u from User u where u.username=:username");
		query.setParameter("username", userName);

		List<User> list = query.list();

		if (list.size() == 0) {
			return false;
		}

		User user = list.get(0);

		List<Integer> filmList = Arrays.asList(filmArray);
		List<Integer> oldWatched = user.getWatchedMovies();

		// TODO : this leads duplicate records for watched movies.
		// But we may see how many times user watched that film.
		// no harm but might change.
		oldWatched.addAll(filmList);

		session.getTransaction().commit();
		session.close();

		return true;
	}

	/**
	 * 
	 * @param userName
	 *            userName of user to get watched films
	 * @return watched film id list
	 */
	private List<Integer> getWatchedFilms(String userName) {
		SessionFactory sf = HibernateUtil.getSessionFactory();
		Session session = sf.openSession();

		Query query = session.createQuery("select u from User u where u.username=:username");
		query.setParameter("username", userName);

		List<User> list = query.list();

		if (list.size() == 0) {
			return null;
		}

		User user = list.get(0);
		session.close();

		return user.getWatchedMovies();
	}

	@Override
	@WebMethod
	public String getWatchedMoviesAsXML(String username) {
		List<Integer> movieIds = getWatchedFilms(username);
		Set<Integer> setItems = new HashSet<Integer>(movieIds);
		movieIds.clear();
		movieIds.addAll(setItems);

		String idList = " ";
		if (movieIds.size() > 0) {
			idList += movieIds.get(0);
			for (int i = 1; i < movieIds.size(); i++)
				idList += " " + movieIds.get(i);
		}
		idList += " ";
		String expression = "//movie[contains('" + idList + "', concat(' ', @id, ' '))]";
		// expression is //*[exists(index-of(('apple', 'orange', 'banana'),
		// @id))]

		InputStream resource = this.getClass().getResourceAsStream("/catalog.xml");
		String result = "";

		try {
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(resource);

			XPathFactory xPathfactory = XPathFactory.newInstance();
			XPath xpath = xPathfactory.newXPath();
			XPathExpression expr = xpath.compile(expression);

			NodeList list = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

			for (int i = 0; i < list.getLength(); i++) {
				Node node = list.item(i);

				TransformerFactory transFactory = TransformerFactory.newInstance();
				Transformer transformer = transFactory.newTransformer();
				StringWriter buffer = new StringWriter();
				transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
				transformer.transform(new DOMSource(node), new StreamResult(buffer));
				result += buffer.toString();
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}

		return result.toString();
	}
}